var obj = {
  a: 1,
}

var obj2 = {
  a: 2,
}

function test(b) {
  console.log(this.a, b);
}

test(); // undefined
// call(thisArg, ...)
// 1. call 方法可以通过第一个参数改变函数内部运行时的 this 指向
// 2. 其余参数通过逗号分隔的方式依次传入函数
// 3. 并且会在改变完 this 指向后直接调用函数
test.call(obj); // 1
test.call(obj, 2); // 1 2
// test.call(obj, 1).call(obj2, 2); // TypeError
console.log('----------');

// apply(thisArg, [...arg])
// 1. call 方法可以通过第一个参数改变函数内部运行时的 this 指向
// 2. 其余参数通过一个数组[]，数组内的元素与函数形参依次对应
// 3. 并且会在改变完 this 指向后直接调用函数
test.apply(obj); // 1
test.apply(obj, [2]); // 1 2
console.log('----------');

// bind(thisArg, ...)
// 1. bind 方法可以通过第一个参数改变函数内部运行时的 this 指向
// 2. 其余参数通过逗号分隔的方式依次传入函数
// 3. bind 不会立即调用函数，会返回一个新的函数
// 4. bind 绑定一个函数的 this 指向后，不能通过bind再次修改次函数 this 指向

// 总结：call 和 apply 参数方面不同，适用于需要改变完 this 指向后立即执行的函数，且它们返回 undefined，bind 适用于不需要立即调用的函数，参数方面与 call保持一致，会返回一个新的函数
var test1 = test.bind(obj, 1);
var test2 = test.bind(obj2, 2);
test1(); // 1 1
test2(); // 2 2
var test2 = test.bind(obj, 1).bind(obj2, 2); // 保持第一次 bind 的 this 指向
test2(); // 1 1